package com.a.b.c.d.e.utils;

import com.documents4j.api.DocumentType;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.spire.presentation.FileFormat;
import com.spire.presentation.ISlide;
import com.spire.presentation.Presentation;
import com.spire.presentation.collections.SlideCollection;
import com.spire.presentation.drawing.BackgroundType;
import com.spire.presentation.drawing.FillFormatType;
import com.spire.presentation.drawing.IImageData;
import com.spire.presentation.drawing.PictureFillType;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @ClassName FileUtils
 * @Author yanshuchun
 * @Date 2021年6月25日08:59:27
 **/
@Slf4j
public class FileUtil {

    public static final String imageTypeDef = "png";
    public static final Integer defaultNum = 50;
    public static final String MARK_URL = "https://wuyou-resource.oss-cn-shanghai.aliyuncs.com/waterMarkA4.png";
    public static final String MARK_URL_PPT = "https://wuyou-resource.oss-cn-shanghai.aliyuncs.com/waterMarkPPT.png";
    public static Image image;
    public static BufferedImage read_ppt;

    static {
        try {
//            image = Image.getInstance(MARK_URL);
            read_ppt = ImageIO.read(new URL(MARK_URL_PPT));
        } catch (Exception e) {
            log.error(e.getMessage());

        }
    }

    /**
     * 线上文件下载
     * soffice --headless --invisible --convert-to pdf:writer_pdf_Export   C:\Users\yanshuchun\Desktop\temp0.ppt  --outdir   C:\Users\yanshuchun\Desktop\
     *
     * @param urlStr 线上地址
     * @return
     */
    public static InputStream downLoadFromUrl(String urlStr) {
        InputStream inputStream = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置超时间为3秒
            conn.setConnectTimeout(30 * 1000);
            // 防止屏蔽程序抓取而返回403错误
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            // 得到输入流
            inputStream = conn.getInputStream();
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return inputStream;
    }

    /**
     * pdf转换成图片
     *
     * @param inputStreams 输入流量集合
     * @param fromUrl      原文件oss 地址
     * @param imgType      图片类型  图片的长度过大的话  只能用png  不能用jpg 及其他，不然报异常
     * @param markOrNot    是不是添加水印
     * @param togetherNum  分组数
     * @return
     */
    public static List<String> pdf2Image(List<InputStream> inputStreams, String fromUrl, String imgType, Boolean markOrNot, Integer togetherNum) {
        List<String> res = new ArrayList<>();
        if (CollectionUtils.isEmpty(inputStreams)) {
            return res;
        }
        int size = inputStreams.size();
        List<BufferedImage> bufferedImageList = new ArrayList<>();
        try {
            if (size == 1) {
                //pdf
                bufferedImageList = getbufferedImage(inputStreams.get(0), markOrNot);
            } else if (size > 1) {
                //ppt  多张pdf 专用(免费jar包使然)
                bufferedImageList = getbufferedImage(inputStreams, markOrNot);
            }
            res = dealBuggeredImage(bufferedImageList, fromUrl, imgType, togetherNum);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return res;
    }


    /**
     * @param inputStreams
     * @param markOrNot
     * @return
     */
    public static List<BufferedImage> getbufferedImage(List<InputStream> inputStreams, Boolean markOrNot) {
        List<BufferedImage> imageTempList = new ArrayList<>();
        Map<Integer, List<BufferedImage>> imageTemp = new HashMap<>();
        List<Integer> weAreFamily = new ArrayList<>();
        for (int i = 0; i < inputStreams.size(); i++) {
            weAreFamily.add(i);
        }
        List<Integer> weAreFamilys = Collections.synchronizedList(weAreFamily);
        try {
            log.info("读取网络数据{}", inputStreams);
            weAreFamilys.stream().forEach(item -> {
                try {
                    imageTemp.put(item, getbufferedImage(inputStreams.get(item), markOrNot));
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            });
            for (int i = 0; i < imageTemp.size(); i++) {
                imageTempList.addAll(imageTemp.get(i));
            }
            log.info("imageTemps(数组) 中的图片信息读取到图片中");

        } catch (Exception e) {
            log.error("转换pdf异常：{}", e.getMessage());
        }
        return imageTempList;
    }

    public static List<BufferedImage> getbufferedImage(InputStream inputStream, Boolean markOrNot) throws Exception {
        BufferedImage[] bufferedImages = getbufferedImageArray(inputStream, markOrNot);
        return Arrays.asList(bufferedImages);
    }

    public static List<String> dealBuggeredImage(List<BufferedImage> imageTempList, String fromUrl, String imgType, Integer togetherNum) throws InterruptedException {
        Map<Integer, ArrayList<BufferedImage>> resMap = new HashMap<>();
        imgType = imgType == null ? imageTypeDef : imgType;
        log.info("按照每页的宽度，页码分组");
        imageGroup(imageTempList, resMap, togetherNum);
        log.info("按照每页的宽度，页码分组完成");
        String[] resMapArray = new String[resMap.size()];
        ThreadFactory fileDeal = new ThreadFactoryBuilder()
                .setNameFormat("PDF2IMAGE-线程池-pool-%d").build();
        ExecutorService executorService = new ThreadPoolExecutor(resMap.size() >= 10 ? 10 : resMap.size(), resMap.size() >= 10 ? 10 : resMap.size(),
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(512), fileDeal, new ThreadPoolExecutor.AbortPolicy());
        String finalImgType = imgType;
        resMap.forEach((key, imageTempArrayList) -> executorService.execute(() -> {
            imageTogether(key, imageTempArrayList, finalImgType, fromUrl, resMapArray);
        }));
        executorService.shutdown();
        executorService.awaitTermination(120, TimeUnit.SECONDS);
        return Arrays.asList(resMapArray);
    }


    /**
     * 图片拼接在一起
     *
     * @param index
     * @param imageTempArrayList
     * @param imgType
     * @param fromUrl
     * @param resMapArray
     */
    static void imageTogether(int index, ArrayList<BufferedImage> imageTempArrayList, String imgType, String fromUrl, String[] resMapArray) {
        List<BufferedImage> imageTemps = Collections.synchronizedList(imageTempArrayList);
        Integer height = imageTemps.parallelStream().collect(Collectors.summingInt(BufferedImage::getHeight));
        Integer width = imageTemps.get(0).getWidth();
        BufferedImage imageResult = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        AtomicInteger y = new AtomicInteger();
        imageTemps.stream().forEach(bufferedImage -> {
            int[] imgRGBArray = new int[width * bufferedImage.getHeight()];
            bufferedImage.getRGB(0, 0,
                    width, bufferedImage.getHeight(), imgRGBArray, 0, width);
            imageResult.setRGB(0, y.get(), width, bufferedImage.getHeight(), imgRGBArray, 0, width);
            y.addAndGet(bufferedImage.getHeight());
        });
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(imageResult, imgType, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        resMapArray[index] = AliyunOssUtil.uploadFile(getNameFromUrl(fromUrl, imgType, index), new ByteArrayInputStream(os.toByteArray()));
    }

    /**
     * 图片文件流 分组【宽度不同，不能在一组，报异常】
     *
     * @param imageTempList
     * @param resMap
     * @param togetherNum
     */
    static void imageGroup(List<BufferedImage> imageTempList, Map<Integer, ArrayList<BufferedImage>> resMap, Integer togetherNum) {
        if (togetherNum!=null || togetherNum == 0) {
            togetherNum = defaultNum;
        }
        ArrayList<BufferedImage> resList = new ArrayList();
        int index = 0;
        if (togetherNum == 1) {
            for (int i = 0; i < imageTempList.size(); i++) {
                resList.add(imageTempList.get(i));
                resMap.put(i, resList);
                resList = new ArrayList<>();
            }
            return;
        }
        BufferedImage bufferedImagePre = imageTempList.get(0);
        resList.add(bufferedImagePre);
        if (imageTempList.size() == 1) {
            resMap.put(index, resList);
            return;
        }
        for (int i = 1; i < imageTempList.size(); i++) {
            BufferedImage bufferedImage = imageTempList.get(i);
            if (bufferedImagePre.getWidth() == bufferedImage.getWidth() && i % togetherNum != 0 && resList.size() < togetherNum) {
                resList.add(bufferedImage);
                if (i == imageTempList.size() - 1) {
                    resMap.put(index++, resList);
                }
            } else {
                resMap.put(index++, resList);
                resList = new ArrayList<>();
                resList.add(bufferedImage);
            }

        }
    }


    /**
     * 从 pdf 输入流 获取 图片流 数组【 水印处理】
     *
     * @param inputStream 文件输入流
     * @throws InterruptedException
     */
    static BufferedImage[] getbufferedImageArray(InputStream inputStream, Boolean markOrNot) throws IOException {
        log.info("in getbufferedImageArray 从 pdf 输入流 获取 图片流 数组【 水印处理】");
        PDDocument pdfFile = PDDocument.load(markOrNot ? mark(inputStream) : inputStream);
        int numPages = pdfFile.getNumberOfPages();
        log.info("pdf页码{}", numPages);
        BufferedImage[] bufferedImages = new BufferedImage[numPages];
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < numPages; i++) {
            list.add(i);
        }
        List<Integer> pageNumList = Collections.synchronizedList(list);
        log.info("将pdf文件对象中的数据读出到 imageTemps(数组)");
        pageNumList.stream().forEach(pageNum -> {
            try {
                PDFRenderer pdfRenderer = new PDFRenderer(pdfFile);
                BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(pageNum, 200);
                bufferedImages[pageNum] = bufferedImage;
            } catch (Exception e) {
                log.info(e.getMessage());
            }
        });
        log.info("out getbufferedImageArray 从 pdf 输入流 获取 图片流 数组【 水印处理】");
        return bufferedImages;
    }


    public static String getNameFromUrl(String fromUrl, String type, int j) {
        String imageName = fromUrl.substring(fromUrl.lastIndexOf("/") + 1);
        if (StringUtils.isNotEmpty(imageName)) {
            int i = imageName.lastIndexOf(".");
            if (i == -1) {
                imageName = imageName + "_" + j + "." + type;
            } else {
                imageName = imageName.substring(0, i) + "_" + j + "." + type;
            }
        } else {
            imageName = System.currentTimeMillis() + "_" + j;
        }
        return imageName;
    }

    /**
     * 输入流转输出流
     *
     * @param in
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 20];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }

    /**
     * 工具类添加水印，方便 但是耗内存 暂时没用
     *
     * @param imageBuffer
     * @return
     * @throws IOException
     */
    public static BufferedImage mark_with3(BufferedImage imageBuffer) throws IOException {
        int width = imageBuffer.getWidth();
        int height = imageBuffer.getHeight();
        InputStream inputStream = downLoadFromUrl("https://wuyou-resource.oss-cn-shanghai.aliyuncs.com/desktop.png");
        BufferedImage markImage = ImageIO.read(inputStream);

        Thumbnails.Builder<BufferedImage> watermark = Thumbnails.of(imageBuffer).scale(1f).watermark((i, i1, i2, i3, i4, i5, i6, i7) -> new Point(width, height), markImage, 0.25f);
        BufferedImage bufferedImage = watermark.asBufferedImage();
        return bufferedImage;

    }

    /**
     * 水印添加到文件内容的上层，覆盖文件内容，可调节透明度
     * 接近底层的加水印，性能好点
     *
     * @param imageBuffer
     * @return
     * @throws IOException
     */
    public static BufferedImage mark(BufferedImage imageBuffer) {
        BufferedImage bufImg = null;
        try {
            // 读取原图片信息
            int srcImgWidth = imageBuffer.getWidth(null);
            int srcImgHeight = imageBuffer.getHeight(null);
            // 加水印
            bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = bufImg.createGraphics();
            g.drawImage(imageBuffer, null, 0, 0);
            //设置透明度
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.66F));
            for (int i = 0; i < srcImgHeight; ) {
                for (int j = 0; j < srcImgWidth; ) {
                    g.drawImage(read_ppt, null, j, i); // 绘制水印
                    j += 550;
                }
                i += 300;
            }
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));

            g.dispose();
            bufImg.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImg;
    }

    /**
     * pdf 内容下方添加水印   doc docx   原生pdf 支持，ppt pptx 转换成的pdf 因为有底色（白色）的原因不支持
     *
     * @param inputStream 要加水印的文件输入流
     * @return
     */
    public static InputStream mark(InputStream inputStream) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            PdfReader reader = new PdfReader(inputStream);
            PdfStamper stamper = new PdfStamper(reader, bos);
            int total = reader.getNumberOfPages() + 1;
            PdfContentByte content;
            PdfGState gs = new PdfGState();
            for (int i = 1; i < total; i++) {
                //在内容下方加水印
                content = stamper.getUnderContent(i);
                gs.setFillOpacity(0.8f);
                content.setGState(gs);
                image.setAlignment(Image.LEFT | Image.TEXTWRAP);
//                image.setRotationDegrees(30);//旋转
                image.setAbsolutePosition(0, 0);
                content.addImage(image);
            }
            stamper.close();
        } catch (Exception e) {
            log.error(e.getMessage());

        }
        return new ByteArrayInputStream(bos.toByteArray());
    }


    //*******************************************************************************************

    /**
     * 水印会在上层（覆盖图片内容）
     *
     * @param url 文件连接地址
     * @return
     */
    public static String addMarkToImage(String url) {
        try {
            String type = url.substring(url.lastIndexOf(".") + 1);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(mark(ImageIO.read(new URL(url))), type, os);
            return AliyunOssUtil.uploadFile(url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf(".") + 1) + type, new ByteArrayInputStream(os.toByteArray()));
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * PPT转化图片
     *
     * @param url         ppt oss 连接
     * @param togetherNum 分组数
     * @param markOrNot   是不是加水印
     * @return
     */
    public static List<String> pptToImageWithWaterMark(String url, Integer togetherNum, Boolean markOrNot) {
        log.info("in pptToImageWithWaterMark url:{},togetherNum:{}，markOrNot:{}", url, togetherNum, markOrNot);
        List<String> res = new ArrayList<>();
        try {
            Presentation presentation = new Presentation();
            InputStream inputStream = downLoadFromUrl(url);
            presentation.loadFromStream(inputStream, FileFormat.PPT);
            int size = presentation.getSlides().size();
            int i;
            if (size % 10 != 0) {
                i = size / 10 + 1;
            } else {
                i = size / 10;
            }
            List<InputStream> arrayList = new ArrayList<>();
            deal(url, arrayList, i, 1, markOrNot);
            res = pdf2Image(arrayList, url, null, false, togetherNum);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        log.info("out pptToImageWithWaterMark res:{}", res);
        return res;
    }

    /**
     * 递归处理 ppt 转 pdf
     *
     * @param url       ppt 线上地址
     * @param res       结果集
     * @param total     总共多少页
     * @param pageNum   当前处理页码
     * @param markOrNot 是不是添加水印
     * @throws Exception
     */
    public static void deal(String url, List<InputStream> res, int total, int pageNum, boolean markOrNot) throws Exception {
        if (pageNum > total) {
            return;
        } else {
            Presentation presentation = new Presentation();
            presentation.loadFromStream(downLoadFromUrl(url), FileFormat.PPT);
            SlideCollection slides = presentation.getSlides();
            int size = slides.size();
            int i1 = size - (total - 1) * 10;
            for (int i = 0; i < (pageNum - 1) * 10; i++) {
                slides.removeAt(0);
            }
            if (total > pageNum) {
                for (int i = 0; i < (total - pageNum - 1) * 10 + i1; i++) {
                    slides.removeAt(slides.size() - 1);
                }
            }
            pageNum++;
            res.add(waterMark(presentation, markOrNot));
            deal(url, res, total, pageNum, markOrNot);
        }
    }

    /**
     * 根据 marOrNot 添加水印或者不添加水印进输出流
     *
     * @param presentationM
     * @param marOrNot
     * @return
     * @throws Exception
     */
    public static InputStream waterMark(Presentation presentationM, boolean marOrNot) throws Exception {
        log.info("in 输出pdf,是不是添加水印？ 答：{}", marOrNot);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //获取水印图片
        if (marOrNot) {
            SlideCollection slidesM = presentationM.getSlides();
            IImageData image = presentationM.getImages().append(read_ppt);
            for (int i = 0; i < slidesM.size(); i++) {
                ISlide iSlide = slidesM.get(i);
                iSlide.getSlideBackground().setType(BackgroundType.CUSTOM);
                iSlide.getSlideBackground().getFill().setFillType(FillFormatType.PICTURE);
                iSlide.getSlideBackground().getFill().getPictureFill().setFillType(PictureFillType.STRETCH);
                iSlide.getSlideBackground().getFill().getPictureFill().getPicture().setEmbedImage(image);
            }
        }
        presentationM.saveToFile(bos, FileFormat.PDF);
        log.info("out 输出pdf,是不是添加水印？ 答答答答答：{}", marOrNot);
        return new ByteArrayInputStream(bos.toByteArray());
    }

    /**
     * 获取url的类型
     *
     * @param url
     * @return
     */
    public static DocumentType getInTypeFromUrl(String url) {
        log.info("in 获取转换类型getInTypeFromUrl");
        DocumentType documentFormat = null;
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        String s = url.toLowerCase();
        if (s.endsWith("doc")) {
            documentFormat = DocumentType.DOC;
        }
        if (s.endsWith("docx")) {
            documentFormat = DocumentType.DOCX;
        }
        if (s.endsWith("pdf")) {
            documentFormat = DocumentType.PDF;
        }
        return documentFormat;
    }

    /**
     * 转换url的类型
     *
     * @return
     */
    public static DocumentType getOUTTypeFromUrl() {
        log.info("in 获取转换类型getOUTTypeFromUrl");
        return DocumentType.PDF;
    }


    public static BufferedImage makeWaterMark(BufferedImage imageBuffer) {
        BufferedImage bufImg = null;
        try {
            // 读取原图片信息
            int srcImgWidth = imageBuffer.getWidth(null);
            int srcImgHeight = imageBuffer.getHeight(null);
            // 加水印
            bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = bufImg.createGraphics();
            g.drawImage(imageBuffer, null, 0, 0);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.66F));
            for (int i = 0; i < srcImgHeight; ) {
                for (int j = 0; j < srcImgWidth; ) {
                    g.drawImage(read_ppt, null, j, i); // 绘制水印
                    j += 550;
                }
                i += 300;
            }
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));

            g.dispose();
            bufImg.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImg;
    }


}
